package p.ithorns.integration.dynamic.module;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import p.ithorns.integration.dynamic.AbstractHandler;
import p.ithorns.integration.dynamic.Handler;

import java.net.MalformedURLException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.ServiceLoader;

/**
 * ModuleLoader
 * TODO
 *
 * @author ithorns
 * @version 1.0
 * @date 2021/10/26 13:22
 */
@Slf4j
public class ModuleLoader implements ApplicationContextAware {

    private ApplicationContext parentContext;

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.parentContext = context;
    }

    public Module load(Path jarPath) {
        log.info("Start to load module: {}", jarPath);

        ModuleClassLoader moduleClassLoader;

        try {
            moduleClassLoader = new ModuleClassLoader(jarPath.toUri().toURL(), parentContext.getClassLoader());
        } catch (MalformedURLException e) {
            throw new RuntimeException("create ModuleClassLoader exception", e);
        }

        // 关键点
        List<AbstractHandler> apis = new ArrayList<>();
        ServiceLoader.load(AbstractHandler.class, moduleClassLoader).forEach(apis::add);
        if (apis.size() <= 0) {
            throw new RuntimeException("module config has and only has one");
        }

        AbstractHandler handler = apis.get(0);
        moduleClassLoader.addOverridePackages(handler.overridePackages());
        ClassLoader curClassLoader = Thread.currentThread().getContextClassLoader();

        // 设置线程的ClassLoader为ModuleClassLoader
        Thread.currentThread().setContextClassLoader(moduleClassLoader);
        ModuleApplicationContext moduleContext = new ModuleApplicationContext();
        moduleContext.setParent(parentContext);
        moduleContext.setClassLoader(moduleClassLoader);
        moduleContext.scan(handler.scanPackages().toArray(new String[0]));
        moduleContext.refresh();
        log.info("Load module success: name={},  jarPath={}", handler.name(), jarPath);

        return new Module(jarPath, handler, moduleContext);
    }
}
